package cn.coder.jdbc.session;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.coder.jdbc.SqlTranction;
import cn.coder.jdbc.config.JdbcConfig;
import cn.coder.jdbc.core.JdbcDataSource;
import cn.coder.jdbc.support.ResultMapper;
import cn.coder.jdbc.util.JdbcUtils;

public abstract class BaseSqlSession {
	private static final Logger logger = LoggerFactory.getLogger(BaseSqlSession.class);

	private final DataSource ds;
	private final JdbcConfig config;
	private SqlTranction tran;

	public BaseSqlSession(JdbcConfig config, String source) {
		this.config = config;
		this.ds = config.getDataSource(source);
	}

	protected SqlTranction newTranction(SqlTranction[] tranctions) throws SQLException {
		if (this.tran != null)
			throw new SQLException("The tranction has already exist.");
		this.tran = new DefaultSqlTranction(this, ds.getConnection(), tranctions);
		logger.debug("Begin tranction:{}", tran.hashCode());
		return this.tran;
	}

	public void endTranction(SqlTranction tranction) {
		if (this.tran != null && this.tran.equals(tranction)) {
			this.tran = null;
			logger.debug("Tranction removed: {}", tranction.hashCode());
		}
	}

	protected <T> T doExecute(ResultMapper<T> mapper) {
		Connection conn = null;
		Statement stmt = null;
		try {
			if (this.tran == null) {
				conn = ds.getConnection();
			} else {
				if (logger.isDebugEnabled())
					logger.debug("Run with tranction:{}", this.tran.hashCode());
				conn = this.tran.Connection();
			}
			stmt = mapper.makeStatement(conn);
			applySettings(stmt);
			T result = mapper.doStatement(stmt);
			handleWarnings(stmt);
			return result;
		} catch (SQLException ex) {
			if (stmt != null)
				logger.error("Error:[" + stmt.toString() + "]", ex);
			throw new RuntimeException("Execute faild:", ex);
		} finally {
			JdbcUtils.closeStatement(stmt);
			if (this.tran == null) {
				JdbcUtils.closeConnection(conn);
			}
		}
	}

	private void applySettings(Statement stmt) throws SQLException {
		if (this.ds instanceof JdbcDataSource) {
			stmt.setQueryTimeout(this.config.getQueryTimeout());
		}
	}

	private void handleWarnings(Statement stmt) throws SQLException {
		// Do Nothing
	}
}
